package com.colin.project.service.impl;

import com.colin.project.bean.HeadImg;
import com.colin.project.bean.User;
import com.colin.project.bean.Video;
import com.colin.project.exception.impl.UpdateUserInfoException;
import com.colin.project.jedis.UserJedisOperate;
import com.colin.project.mapper.FileMapper;
import com.colin.project.mapper.UserMapper;
import com.colin.project.service.UserService;
import com.colin.project.util.EncipherUtil;
import com.colin.project.util.FileUtil;
import com.colin.project.util.MailUtils;
import com.colin.project.util.response.ResponseResult;
import com.colin.project.util.response.Status;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import javax.servlet.http.HttpSession;
import java.io.File;
import java.io.InputStream;
import java.sql.Timestamp;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;
    @Autowired
    private FileMapper fileMapper;
    @Autowired
    private UserJedisOperate userJedisOperate;
    @Autowired
    private EncipherUtil encipherUtil;
    @Autowired
    private ResponseResult<String> responseResult;
    @Autowired
    private JedisPool jedisPool;

    /**
     * 用户的默认头像路径
     */
    @Value("${my.project.user.head-img.default-mapping-path}")
    private String defaultImgPath;

    /**
     * 用户头像上传存储的本地真实路径前缀
     */
    @Value("${my.project.user.head-img.resource-location}")
    private String headImgResourceLocationPrefix;
    /**
     * 程序访问头像的虚拟路径前缀
      */
    @Value("${my.project.user.head-img.resource-handler}")
    private String headImgResourceHandlerPrefix;

    /**
     * 视频上传存储的本地真实路径前缀
     */
    @Value("${my.project.user.video.resource-location}")
    private String videoResourceLocationPrefix;
    /**
     * 程序访问视频的虚拟路径前缀
     */
    @Value("${my.project.user.video.resource-handler}")
    private String videoResourceHandlerPrefix;


    @Override
    public ResponseResult<String> checkUsername(String username) {
        //校验用户名是否符合用户名格式
        Pattern pattern = Pattern.compile("^[a-zA-Z0-9]{3,12}$");
        Matcher matcher = pattern.matcher(username);
        if (!matcher.matches()) {
            responseResult.setStatus(Status.PATTERN_ERROR);
            responseResult.setMessage("用户名格式不正确");
            return responseResult;
        }

        //验证用户名是否已存在
        Integer id = userMapper.selectIdByUsername(username);
        if (id == null) {
            responseResult.setStatus(Status.SUCCESS);
            responseResult.setMessage("用户名可以使用");
        } else {
            responseResult.setStatus(Status.USERNAME_EXISTS);
            responseResult.setMessage("用户名已存在");
        }
        return responseResult;
    }


    /**
     * 通过邮箱发送验证码
     * @param email 目标邮箱
     * @param flag 标记 因为注册和忘记密码时都会用到改接口，所以用flag来区分。flag == 0 代表注册时的逻辑， flag == 1 代表忘记密码时的逻辑
     * @return
     */
    @Override
    public ResponseResult<String> sendEmail(String email, Integer flag) {
        //校验邮箱是否符合邮箱格式
        Pattern compile = Pattern.compile("^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$");
        Matcher matcher = compile.matcher(email);
        //给邮箱发验证码
        //生成六位验证码
        if (matcher.matches()) {
            String random = Math.random() + "";
            String randomString = random.substring(2,8);
            Integer id = userMapper.selectIdByEmail(email);
            //进行账号注册逻辑时 且 该邮箱已被注册
            if(flag == 0 && id != null) {
                responseResult.setStatus(Status.EMAIL_EXISTS);
                responseResult.setMessage("邮箱已存在");
                return responseResult;
            }
            //进行忘记密码逻辑时 且 该邮箱未被注册
            if(flag == 1 && id == null) {
                responseResult.setStatus(Status.ERROR);
                responseResult.setMessage("邮箱未注册，请先注册");
                return responseResult;
            }
            MailUtils.sendMail(email, "验证码:" + randomString, "验证码");
            //验证码需要过期时间，因此交给Redis存储过期时间的key,邮箱为key，验证码为valve
            if ("success".equals(userJedisOperate.generateCode(email, randomString))) {
                responseResult.setStatus(Status.SUCCESS);
                responseResult.setMessage("验证码生成成功");
            } else {
                responseResult.setStatus(Status.ERROR);
                responseResult.setMessage("验证码生成失败");
            }
            return responseResult;

        }
        responseResult.setStatus(Status.PATTERN_ERROR);
        responseResult.setMessage("邮箱格式不正确");
        return responseResult;
    }

    /**
     *
     * @param code 验证码
     * @param email 邮箱
     * @return
     */
    @Override
    public ResponseResult<String> checkCode(String code, String email) {
        if("success".equals(userJedisOperate.checkCode(code,email))){
            responseResult.setStatus(Status.SUCCESS);
            responseResult.setMessage("验证码校验成功");
        } else {
            responseResult.setStatus(Status.ERROR);
            responseResult.setMessage("验证码校验失败");
        }
        return responseResult;
    }

    /**
     *
     * @param user 用于注册的用户数据
     * @return true / false
     */
    @Override
    public Boolean doRegister(User user) {
        //密码加密
        String password = user.getPassword();
        String finalPassword = encipherUtil.doEncipher(password);
        user.setPassword(finalPassword);
        //补齐缺少属性
        Timestamp timestamp = new Timestamp(System.currentTimeMillis());
        user.setRegisterTime(timestamp);
        user.setUpdateTime(timestamp);
        user.setHeadImg(this.defaultImgPath);
        user.setUserType(0);
        //插入数据库
        return userMapper.insertUser(user);
    }

    @Override
    public Boolean changePassword(String email, String password) {
        Boolean updateResult = false;
        Pattern compile = Pattern.compile("^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$");
        Matcher matcher = compile.matcher(email);

        if (matcher.matches()) {
            String finalPassword = encipherUtil.doEncipher(password);
            updateResult = userMapper.updatePasswordByEmail(email, finalPassword);
        }

        return updateResult;
    }

    @Override
    public User doLogin(String username, String password) {
        String finalPassword = encipherUtil.doEncipher(password);
        User user = userMapper.selectUserByUsernameAndPassword(username, finalPassword);
        if (user != null) {
            Timestamp currentTime = new Timestamp(System.currentTimeMillis());
            userMapper.updateLastLoginTime(currentTime, username);
            return user;
        }
        return null;
    }

    /**
     * 更换头像
     * @param multipartFile
     * @param session
     * @param md5
     * @return
     */
    @Override
    @Transactional//借助@Transactional声明式事务去回滚
    public ResponseResult<String> headImgUpload(MultipartFile multipartFile, HttpSession session, String md5) {
        User loginUser = null;
        HeadImg headImg = null;
        Jedis connection = null;
        try {
            loginUser = (User) session.getAttribute("loginUser");
            String username = loginUser.getUsername();

            //1.接收文件并存储
            //文件原始名称
            String originalFilename = multipartFile.getOriginalFilename();
            //上传文件的字节输入流
            InputStream inputStream = multipartFile.getInputStream();
            //因为重名文件会覆盖，所以
            //（1）为每个用户创建一个存储用的文件夹，文件夹名称为用户名
            //（2）每个文件存储时命名加上当前时间戳
            // D:/headImg/ice
            long timestamp = System.currentTimeMillis();
            //通过自己封装的文件读写方法 将文件写入本地磁盘 并返回文件最终名称
            String finalFileName = FileUtil.readWriteFile(
                    headImgResourceLocationPrefix,
                    username,
                    originalFilename,
                    timestamp,
                    inputStream
            );


            File file1 = new File(headImgResourceLocationPrefix + username + "/" + finalFileName);
            long length = file1.length();

            //2.将文件的相关信息存入数据库
            headImg = new HeadImg.HeadImgBuilder()
                    .user(loginUser)
                    .uploadTime(new Timestamp(timestamp))
                    .originalPath(headImgResourceLocationPrefix + username + "/" + finalFileName)
                    .mappingPath(headImgResourceHandlerPrefix + username + "/" + finalFileName)
                    .imgSize(length + "")
                    .imgType(FileUtil.getSuffixName(originalFilename).substring(1))
                    .originalName(finalFileName)
                    .build();

            //添加头像数据
            fileMapper.insertHeadImg(headImg);
            //更新用户头像
            userMapper.updateHeadImgById(loginUser.getId(), headImg.getMappingPath());

            connection = jedisPool.getResource();
            connection.zadd("md5FileCache", headImg.getId(), md5);

            loginUser.setHeadImg(headImg.getMappingPath());
            session.setAttribute("loginUser", loginUser);
        } catch (Exception e) {
            File file = null;
            if (headImg != null) {
                file = new File(headImg.getOriginalPath());
            }
            if (file != null && file.exists()) {
                file.delete();
            }
            throw new RuntimeException(e);
        } finally {
            if (connection != null) {
                connection.close();
            }
        }

        // 3. 返回结果
        responseResult.setStatus(Status.SUCCESS);
        return responseResult;
    }

    @Override
    @Transactional
    public ResponseResult<String> updatePersonalInfo(User user, HttpSession session) {
        try {
            Timestamp timestamp = new Timestamp(System.currentTimeMillis());
            user.setUpdateTime(timestamp);

            if (user.getPassword() != null) {
                user.setPassword(encipherUtil.doEncipher(user.getPassword()));
            }

            userMapper.updateUserById(user);
            user = userMapper.selectUserById(user.getId());
            session.setAttribute("loginUser", user);

            responseResult.setStatus(Status.SUCCESS);
            responseResult.setMessage("修改成功");
        } catch (Exception e) {
            responseResult.setStatus(Status.ERROR);
            responseResult.setMessage("服务器异常，请重试");
            throw new UpdateUserInfoException("修改个人信息异常");
        }

        return responseResult;
    }

    /**
     * 上传视频
     * @param multipartFile
     * @param session
     * @param md5
     * @return
     */
    @Override
    @Transactional// 借助@Transactional声明式事务去回滚
    public ResponseResult<String> videoUpload(MultipartFile multipartFile, HttpSession session, String md5) {
        User loginUser = null;
        Video video = null;
        Jedis connection = null;
        try {
            loginUser = (User) session.getAttribute("loginUser");
            String username = loginUser.getUsername();

            // 1.接收文件并存储
            // 文件原始名称
            String originalFilename = multipartFile.getOriginalFilename();
            // 上传文件的字节输入流
            InputStream inputStream = multipartFile.getInputStream();
            // 因为重名文件会覆盖，所以
            //（1）为每个用户创建一个存储用的文件夹，文件夹名称为用户名
            //（2）每个文件存储时命名加上当前时间戳
            // D:/headImg/ice
            long timestamp = System.currentTimeMillis();
            // 通过自己封装的文件读写方法 将文件写入本地磁盘 并返回文件最终名称
            String finalFileName = FileUtil.readWriteFile(
                    videoResourceLocationPrefix,
                    username,
                    originalFilename,
                    timestamp,
                    inputStream
            );


            File file1 = new File(videoResourceLocationPrefix + username + "/" + finalFileName);
            long length = file1.length();

            //2.将文件的相关信息存入数据库
            video = new Video.VideoBuilder()
                    .user(loginUser)
                    .uploadTime(new Timestamp(timestamp))
                    .originalPath(videoResourceLocationPrefix + username + "/" + finalFileName)
                    .mappingPath(videoResourceHandlerPrefix + username + "/" + finalFileName)
                    .videoSize(length + "")
                    .videoType(FileUtil.getSuffixName(originalFilename).substring(1))
                    .originalName(finalFileName)
                    .build();
            fileMapper.insertVideo(video);

            connection = jedisPool.getResource();
            connection.zadd("md5FileCache", video.getId(), md5);

            session.setAttribute("loginUser", loginUser);
        } catch (Exception e) {
            File file = null;
            if (video != null) {
                file = new File(video.getOriginalPath());
            }
            if (file != null && file.exists()) {
                file.delete();
            }
            throw new RuntimeException(e);
        } finally {
            if (connection != null) {
                connection.close();
            }
        }

        // 3. 返回结果
        responseResult.setStatus(Status.SUCCESS);
        return responseResult;
    }
}
